/*
 * Copyright 2004-2005 The Apache Software Foundation or its licensors,
 *                     as applicable.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jackrabbit.core.query.lucene;

import org.apache.lucene.util.Constants;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.OutputStream;
import org.apache.lucene.store.InputStream;

import java.io.IOException;
import java.io.File;
import java.util.Map;
import java.util.HashMap;

/**
 * This is a wrapper class to provide lock creation in the index directory.
 * <p/>
 * As of lucene 1.3 lock files are created in the users temp directory, which is
 * not very user friendly when the system crashed. One has to look up the temp
 * directory and find out which lock file belongs to which lucene index.
 * <p/>
 * This wrapper class delegates most operations to the default FSDirectory
 * implementation of lucene but has its own makeLock() implementation.
 */
class FSDirectory extends Directory {

    /**
     * Flag indicating whether locks are disabled
     */
    private static final boolean DISABLE_LOCKS =
            Boolean.getBoolean("disableLuceneLocks") || Constants.JAVA_1_1;

    /**
     * The actual FSDirectory implementation
     */
    private final org.apache.lucene.store.FSDirectory delegatee;

    /**
     * The directory where this index is located
     */
    private final File directory;

    /**
     * internal ref count for cached FSDirectories
     */
    private int refCount = 0;

    /**
     * map where the cached FSDirectories are stored
     */
    private static final Map directories = new HashMap();

    /**
     * Creates a new FSDirectory based on a lucene FSDirectory instance.
     *
     * @param delegatee the lucene FSDirectory instance.
     * @param directory the directory where this index is located.
     */
    private FSDirectory(org.apache.lucene.store.FSDirectory delegatee, File directory) {
        this.delegatee = delegatee;
        this.directory = directory;
    }

    /**
     * Returns the directory instance for the named location.
     * <p/>
     * <p>Directories are cached, so that, for a given canonical path, the same
     * FSDirectory instance will always be returned.  This permits
     * synchronization on directories.
     *
     * @param file   the path to the directory.
     * @param create if true, create, or erase any existing contents.
     * @return the FSDirectory for the named file.
     */
    public static FSDirectory getDirectory(File file, boolean create)
            throws IOException {
        FSDirectory dir;
        synchronized (directories) {
            dir = (FSDirectory) directories.get(file.getCanonicalPath());
            if (dir == null) {
                dir = new FSDirectory(org.apache.lucene.store.FSDirectory.getDirectory(file, create), file);
                directories.put(file.getCanonicalPath(), dir);
            }
        }
        synchronized (dir) {
            dir.refCount++;
        }
        return dir;
    }

    /**
     * Creates a lock file in the current index directory.
     *
     * @param name the name of the lock file.
     * @return a Lock object with the given name.
     */
    public Lock makeLock(String name) {
        final File lockFile = new File(directory, name);
        return new Lock() {
            public boolean obtain() throws IOException {
                if (DISABLE_LOCKS) {
                    return true;
                }
                return lockFile.createNewFile();
            }

            public void release() {
                if (DISABLE_LOCKS) {
                    return;
                }
                lockFile.delete();
            }

            public boolean isLocked() {
                if (DISABLE_LOCKS) {
                    return false;
                }
                return lockFile.exists();
            }

            public String toString() {
                return "Lock@" + lockFile;
            }
        };
    }

    /**
     * @inheritDoc
     */
    public synchronized void close()
            throws IOException {
        delegatee.close();
        if (--refCount <= 0) {
            // really close
            synchronized (directories) {
                directories.remove(directory.getCanonicalPath());
            }
        }
    }

    /**
     * @inheritDoc
     */
    public OutputStream createFile(String name)
            throws IOException {
        return delegatee.createFile(name);
    }

    /**
     * @inheritDoc
     */
    public void deleteFile(String name)
            throws IOException {
        delegatee.deleteFile(name);
    }

    /**
     * @inheritDoc
     */
    public boolean fileExists(String name)
            throws IOException {
        return delegatee.fileExists(name);
    }

    /**
     * @inheritDoc
     */
    public long fileLength(String name)
            throws IOException {
        return delegatee.fileLength(name);
    }

    /**
     * @inheritDoc
     */
    public long fileModified(String name)
            throws IOException {
        return delegatee.fileModified(name);
    }

    /**
     * @inheritDoc
     */
    public String[] list()
            throws IOException {
        return delegatee.list();
    }

    /**
     * @inheritDoc
     */
    public InputStream openFile(String name)
            throws IOException {
        return delegatee.openFile(name);
    }

    /**
     * @inheritDoc
     */
    public void renameFile(String from, String to)
            throws IOException {
        delegatee.renameFile(from, to);
    }

    /**
     * @inheritDoc
     */
    public void touchFile(String name)
            throws IOException {
        delegatee.touchFile(name);
    }
}
